#include "stm32f4xx.h"                  // Device header
#include "stm32f4xx_conf.h"
#include "MyI2C.h"

/*
 *函数简介:软件I2C专用us延时
 *参数说明:延时时长,单位1/21us
 *返回类型:无
 *备注:参数范围:0~4294967295
 */
void MyI2C_Delay_us(uint32_t us)
{
	uint32_t temp;	    	 
	SysTick->LOAD=us; 							//时间加载，我们要延时n倍的us, 1us是一个fac_ua周期，所以总共要延时的周期值为二者相乘最后送到Load中。		 
	SysTick->VAL=0x00;        					//清空计数器
	SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;	//开启使能位 开始倒数
	do temp=SysTick->CTRL;
	while((temp&0x01) && !(temp&(1<<16)));		//用来判断 systick 定时器是否还处于开启状态，然后在等待时间到达，也就是数到0的时候,此时第十六位设置为1
	SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk;	//关闭计数器使能位
	SysTick->VAL=0x00;       					//清空计数器
}

/*
 *函数简介:软件I2C写SCL电平
 *参数说明:软件I2C配置结构体
 *参数说明:高低电平,0-低电平 1-高电平
 *返回类型:无
 *备注:使用前需要先定义结构体并初始化
 *备注:由于I2C速度较慢,在更改电平之后需要加入延时
 */
void MyI2C_SCL(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t x)
{
	GPIO_WriteBit(MyI2C_InitStructure->MyI2C_SCL_GPIOx,MyI2C_InitStructure->MyI2C_SCL_Pin,(BitAction)(x));
	MyI2C_Delay_us(12);//延时
}

/*
 *函数简介:软件I2C写SDA电平
 *参数说明:软件I2C配置结构体
 *参数说明:高低电平,0-低电平 1-高电平
 *返回类型:
 *备注:使用前需要先定义结构体并初始化
 *备注:由于I2C速度较慢,在更改电平之后需要加入延时
 */
void MyI2C_SDA(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t x)
{
	GPIO_WriteBit(MyI2C_InitStructure->MyI2C_SDA_GPIOx,MyI2C_InitStructure->MyI2C_SDA_Pin,(BitAction)(x));
	MyI2C_Delay_us(12);//延时
}

/*
 *函数简介:软件I2C读SDA电平
 *参数说明:软件I2C配置结构体
 *返回类型:高低电平,0-低电平 1-高电平
 *备注:使用前需要先定义结构体并初始化
 *备注:由于I2C速度较慢,在电平更改之后需要加入延时,读取时也可以不加
 */
uint8_t MyI2C_Read_SDA(MyI2C_InitTypedef *MyI2C_InitStructure)
{
	MyI2C_Delay_us(12);//延时
	uint8_t SDA_Status=GPIO_ReadInputDataBit(MyI2C_InitStructure->MyI2C_SDA_GPIOx,MyI2C_InitStructure->MyI2C_SDA_Pin);
	return SDA_Status;
}

/*
 *函数简介:软件I2C初始化
 *参数说明:软件I2C配置结构体
 *返回类型:无
 *备注:使用前需要先定义结构体并初始化
 */
void MyI2C_Init(MyI2C_InitTypedef *MyI2C_InitStructure)
{
	RCC_AHB1PeriphClockCmd(MyI2C_InitStructure->MyI2C_SCL_RCC,ENABLE);//开启SCL时钟
	RCC_AHB1PeriphClockCmd(MyI2C_InitStructure->MyI2C_SDA_RCC,ENABLE);//开启SDA时钟
	
	GPIO_InitTypeDef GPIO_InitStructure;
 	GPIO_InitStructure.GPIO_Mode=GPIO_Mode_OUT;
	GPIO_InitStructure.GPIO_OType=GPIO_OType_OD;//开漏输出
	GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;//默认上拉
	GPIO_InitStructure.GPIO_Pin=MyI2C_InitStructure->MyI2C_SCL_Pin;
	GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;
 	GPIO_Init(MyI2C_InitStructure->MyI2C_SCL_GPIOx,&GPIO_InitStructure);//配置SCL
	GPIO_InitStructure.GPIO_Pin=MyI2C_InitStructure->MyI2C_SDA_Pin;
 	GPIO_Init(MyI2C_InitStructure->MyI2C_SDA_GPIOx,&GPIO_InitStructure);//配置SDA
	
	MyI2C_SCL(MyI2C_InitStructure,1);//初始化SCL
	MyI2C_SDA(MyI2C_InitStructure,1);//初始化SDA
}

/*
 *函数简介:软件I2C起始
 *参数说明:软件I2C配置结构体
 *返回类型:无
 *I2C协议:
 *	SCL	~~~~/---\___
 *	SDA	~~/---\_____
 */
void MyI2C_Start(MyI2C_InitTypedef *MyI2C_InitStructure)
{
	MyI2C_SDA(MyI2C_InitStructure,1);
	MyI2C_SCL(MyI2C_InitStructure,1);//复位SCL和SDA
	MyI2C_SDA(MyI2C_InitStructure,0);//起始
	MyI2C_SCL(MyI2C_InitStructure,0);//准备接收数据
}

/*
 *函数简介:软件I2C终止
 *参数说明:软件I2C配置结构体
 *返回类型:无
 *I2C协议:
 *	SCL	____/-----
 *	SDA	~~\___/---
 */
void MyI2C_Stop(MyI2C_InitTypedef *MyI2C_InitStructure)
{
	MyI2C_SDA(MyI2C_InitStructure,0);
	MyI2C_SCL(MyI2C_InitStructure,1);//准备接收停止信号
	MyI2C_SDA(MyI2C_InitStructure,1);//终止
}

/*
 *函数简介:软件I2C产生应答
 *参数说明:软件I2C配置结构体
 *参数说明:是否应答,0-NAck非应答 1-Ack应答
 *返回类型:无
 *I2C协议:
 *	Ack:
 *		SCL____/-\___
 *		SDA~~\___/---
 *	NAck:
 *		SCL____/-\___
 *		SDA~~/-------
 */
void MyI2C_SendAck(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t AckBit)
{
	MyI2C_SDA(MyI2C_InitStructure,!AckBit);//发送应答情况
	MyI2C_SCL(MyI2C_InitStructure,1);//产生时钟
	MyI2C_SCL(MyI2C_InitStructure,0);
	MyI2C_SDA(MyI2C_InitStructure,1);//释放总线
}

/*
 *函数简介:软件I2C接收应答
 *参数说明:软件I2C配置结构体
 *返回类型:是否应答,0-NAck非应答 1-Ack应答
 *I2C协议:
 *	SCL	____/-\___
 *	SDA	~~/-|==|~~
 *			(  )从机
 */
uint8_t MyI2C_ReceiveAck(MyI2C_InitTypedef *MyI2C_InitStructure)
{
	uint8_t AckBit;//应答标志位
	
	MyI2C_SDA(MyI2C_InitStructure,1);//释放SDA总线
	MyI2C_SCL(MyI2C_InitStructure,1);//提示从机发送Ack
	
	AckBit=!MyI2C_Read_SDA(MyI2C_InitStructure);//接收应答
	
	MyI2C_SCL(MyI2C_InitStructure,0);//置SCL为0,准备下一轮发送
	return AckBit;//返回应答情况
}

/*
 *函数简介:软件I2C发送一个8bits数据
 *参数说明:软件I2C配置结构体
 *参数说明:8bits发送数据
 *返回类型:无
 *I2C协议:
 *	SCL	____/-\___/-\___/-\___/-\___/-\___/-\___/-\___/-\___
 *	SDA	~~x=====x=====x=====x=====x=====x=====x=====x=====~~
 */
void MyI2C_Send8bits(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t Byte)
{
	for(uint8_t i=0;i<8;i++)
	{
		MyI2C_SDA(MyI2C_InitStructure,Byte&(0x80>>i));//发送数据每一位,高位先行
		MyI2C_SCL(MyI2C_InitStructure,1);//产生时钟
		MyI2C_SCL(MyI2C_InitStructure,0);
	}
	
	MyI2C_SDA(MyI2C_InitStructure,1);//释放SDA总线
}

/*
 *函数简介:软件I2C读取一个8bits数据
 *参数说明:软件I2C配置结构体
 *返回类型:8bits接收数据
 *I2C协议:
 *	SCL	____/-\_/-\_/-\_/-\_/-\_/-\_/-\_/-\___
 *	SDA	~~/-|==============================|~~
 *			(							   )从机
 */
uint8_t MyI2C_Receive8bits(MyI2C_InitTypedef *MyI2C_InitStructure)
{
	uint8_t Byte=0x00;
	MyI2C_SDA(MyI2C_InitStructure,1);//释放SDA总线
	
	for(uint8_t i=0;i<8;i++)//读取数据每一位,高位先行
	{
		MyI2C_SCL(MyI2C_InitStructure,1);//产生时钟
		if(MyI2C_Read_SDA(MyI2C_InitStructure)){Byte|=(0x80>>i);}//读取SDA
		MyI2C_SCL(MyI2C_InitStructure,0);
	}
	
	return Byte;
}

/*
 *函数简介:软件I2C发送一个字节数据
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *参数说明:8bits发送数据
 *返回类型:无
 *备注:无
 */
void MyI2C_SendByte(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t Address,uint8_t Byte)
{
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,Address<<1);//发送设备地址(写)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	MyI2C_Send8bits(MyI2C_InitStructure,Byte);//发送数据
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
}

/*
 *函数简介:软件I2C发送数据
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *参数说明:发送数据数组
 *参数说明:发送数据数组长度
 *返回类型:无
 *备注:无
 */
void MyI2C_SendData(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t Address,uint8_t *Data,uint16_t Length)
{
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,Address<<1);//发送设备地址(写)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	for(uint16_t i=0;i<Length;i++)
	{
		MyI2C_Send8bits(MyI2C_InitStructure,Data[i]);//发送数据
		while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	}
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
}

/*
 *函数简介:软件I2C写寄存器
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *参数说明:8bits寄存器地址
 *参数说明:8bits数据
 *返回类型:无
 *备注:无
 */
void MyI2C_WriteRegister(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t SlaveAddress,uint8_t RegAddress,uint8_t Data)
{
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,SlaveAddress<<1);//发送从机地址(写)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	MyI2C_Send8bits(MyI2C_InitStructure,RegAddress);//发送寄存器地址
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	MyI2C_Send8bits(MyI2C_InitStructure,Data);//发送数据
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
}

/*
 *函数简介:软件I2C接收一个字节数据
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *返回类型:8bits接收数据
 *备注:无
 */
uint8_t MyI2C_ReceiveByte(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t Address)
{
	uint8_t Byte=0;
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,(Address<<1) | 0x01);//发送设备地址(读)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	Byte=MyI2C_Receive8bits(MyI2C_InitStructure);//接收数据
	MyI2C_SendAck(MyI2C_InitStructure,0);//发送NAck
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
	return Byte;
}

/*
 *函数简介:软件I2C接收数据
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *参数说明:接收数据数组
 *参数说明:接收数据数组长度
 *返回类型:8bits接收数据
 *备注:无
 */
void MyI2C_ReceiveData(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t Address,uint8_t *Data,uint16_t Length)
{
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,(Address<<1) | 0x01);//发送设备地址(读)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	for(uint16_t i=0;i<Length-1;i++)
	{
		Data[i]=MyI2C_Receive8bits(MyI2C_InitStructure);//接收数据
		MyI2C_SendAck(MyI2C_InitStructure,1);//发送Ack
	}
	Data[Length-1]=MyI2C_Receive8bits(MyI2C_InitStructure);//接收最后一个数据
	MyI2C_SendAck(MyI2C_InitStructure,0);//发送NAck
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
}

/*
 *函数简介:软件I2C读寄存器
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *参数说明:8bits寄存器地址
 *返回类型:8bits数据
 *备注:无
 */
uint8_t MyI2C_ReadRegister(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t SlaveAddress,uint8_t RegAddress)
{
	uint8_t Byte=0;
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,SlaveAddress<<1);//发送从机地址(写)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	MyI2C_Send8bits(MyI2C_InitStructure,RegAddress);//发送寄存器地址
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	MyI2C_Start(MyI2C_InitStructure);//重复起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,(SlaveAddress<<1) | 0x01);//发送设备地址(读)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	Byte=MyI2C_Receive8bits(MyI2C_InitStructure);//接收数据
	MyI2C_SendAck(MyI2C_InitStructure,0);//发送NAck
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
	return Byte;
}

/*
 *函数简介:软件I2C连续读寄存器
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *参数说明:8bits寄存器首地址
 *参数说明:接收数据数组
 *参数说明:接收数据数组长度
 *返回类型:无
 *备注:无
 */
void MyI2C_ContinuousReadRegister(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t SlaveAddress,uint8_t RegStartAddress,uint8_t *ReceiveData,uint16_t Length)
{
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,SlaveAddress<<1);//发送从机地址(写)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	MyI2C_Send8bits(MyI2C_InitStructure,RegStartAddress);//发送寄存器地址
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	MyI2C_Start(MyI2C_InitStructure);//重复起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,(SlaveAddress<<1) | 0x01);//发送设备地址(读)
	while(MyI2C_ReceiveAck(MyI2C_InitStructure)!=1);//等待Ack应答
	
	for(uint16_t i=0;i<Length-1;i++)
	{
		ReceiveData[i]=MyI2C_Receive8bits(MyI2C_InitStructure);//接收数据
		MyI2C_SendAck(MyI2C_InitStructure,1);//发送Ack
	}
	ReceiveData[Length-1]=MyI2C_Receive8bits(MyI2C_InitStructure);//接收最后一个数据
	MyI2C_SendAck(MyI2C_InitStructure,0);//发送NAck
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
}

/*
 *函数简介:软件I2C检测设备状态
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *返回类型:工作状态,0-设备不存在或异常工作 1-设备正常工作
 *备注:无
 */
uint8_t MyI2C_CheckDevice(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t Address)
{
	uint8_t WorkStatus;//工作标志位
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,Address<<1);//发送设备地址(写)
	WorkStatus=MyI2C_ReceiveAck(MyI2C_InitStructure);//检测Ack应答
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
	return WorkStatus;
}

/*
 *函数简介:软件I2C查询Who Am I寄存器
 *参数说明:软件I2C配置结构体
 *参数说明:7bits从机地址
 *参数说明:8bits Who Am I寄存器地址
 *返回类型:Who Am I寄存器的值,0-设备不存在或异常工作 其他-返回寄存器值
 *备注:无
 */
uint8_t MyI2C_CheckWhoAmI(MyI2C_InitTypedef *MyI2C_InitStructure,uint8_t SlaveAddress,uint8_t WhoAmIAddress)
{
	uint8_t WhoAmI;
	MyI2C_Start(MyI2C_InitStructure);//起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,SlaveAddress<<1);//发送设备地址(写)
	if(MyI2C_ReceiveAck(MyI2C_InitStructure)==0)return 0;//检测Ack应答
	MyI2C_Send8bits(MyI2C_InitStructure,WhoAmIAddress);//发送寄存器地址
	if(MyI2C_ReceiveAck(MyI2C_InitStructure)==0)return 0;//检测Ack应答
	
	MyI2C_Start(MyI2C_InitStructure);//重复起始
	
	MyI2C_Send8bits(MyI2C_InitStructure,(SlaveAddress<<1) | 0x01);//发送设备地址(读)
	if(MyI2C_ReceiveAck(MyI2C_InitStructure)==0)return 0;//检测Ack应答
	
	WhoAmI=MyI2C_Receive8bits(MyI2C_InitStructure);//接收数据
	MyI2C_SendAck(MyI2C_InitStructure,0);//发送NAck
	
	MyI2C_Stop(MyI2C_InitStructure);//终止
	return WhoAmI;
}
